// 主任务负责，底层数据分发到子任务，并作电源管理、按键检测、数据采集。数据的总来源。
#include "version.h"
#include "gnss.h"
#include "media.h"
#include "gps.h"
#include "gprs.h"
#include "ui.h"
#include "oled_12832.h"
#include "utc_timer.h"

// 主任务句柄
HANDLE MainTaskHandle  = NULL;
HANDLE GprsTaskHandle  = NULL;
HANDLE GnssTaskHandle  = NULL;
HANDLE MediaTaskHandle = NULL;
HANDLE UiTaskHandle    = NULL;

// 原始消息转化为简化消息 不做速度漂移过滤
static bool Gnss_Original2Brief(GPS_Info_t *AInfo, GnssNmea_t *AGnssNmea)
{
    static uint16_t  altitudeBkp = 0; // 输出的海拔偶尔跳变
    bool   result = false;

    if (AInfo && AGnssNmea)
    {
        uint32_t Result = 0;
        uint32_t Temp= 0;
        uint8_t  isFixed;

        if (AInfo->rmc.valid) // 定位消息有效
        {
            AGnssNmea->genTime = RTC_MakeUnixStamp(StampType2000,
                                                AInfo->rmc.date.year + 2000,
                                                AInfo->rmc.date.month, 
                                                AInfo->rmc.date.day,
                                                AInfo->rmc.time.hours, 
                                                AInfo->rmc.time.minutes, 
                                                AInfo->rmc.time.seconds);

            AGnssNmea->genTime += 28800; // 东8时区 TODO用宏定义表示
        }
        else
            AGnssNmea->Speed = 0;
            

        // 定位质量 [0]GPS [1]BDS
        isFixed = AInfo->gsa[0].fix_type > AInfo->gsa[1].fix_type ? AInfo->gsa[0].fix_type : AInfo->gsa[1].fix_type;
        if (isFixed == 2)
            AGnssNmea->FixQuality = FixQ_2DFix;
        else if (isFixed == 3)
        {
            if (AInfo->gga.fix_quality == 1)
                AGnssNmea->FixQuality = FixQ_3DFix;
            else if (AInfo->gga.fix_quality == 2)
                AGnssNmea->FixQuality = FixQ_DGPSFix;
            else if (AInfo->gga.fix_quality >= 3)
                AGnssNmea->FixQuality = FixQ_PPSFix;
        }
        else
            AGnssNmea->FixQuality = FixQ_NoFix;

        // 定位状态
        AGnssNmea->IsVaild = AInfo->rmc.valid; // 1:有效

        // 速度转换
        if (AInfo->rmc.speed.scale)
            AGnssNmea->Speed = ((AInfo->rmc.speed.value * 1852) / 100) / AInfo->rmc.speed.scale;
        else
            AGnssNmea->Speed = 0;       
        
        // 纬度转换 有正负
        Temp = AInfo->rmc.latitude.value % 1000000;
        Result = AInfo->rmc.latitude.value - Temp;
        AGnssNmea->Latitude = Result + Temp * 100 / 60;

        // 经度转换 有正负
        Temp = AInfo->rmc.longitude.value % 1000000;
        Result = AInfo->rmc.longitude.value - Temp;
        AGnssNmea->Longitude = Result + Temp * 100 / 60;

        // 海拔转换 有正负
        if (AInfo->gga.altitude.scale)
        {
            AGnssNmea->Altitude = AInfo->gga.altitude.value / AInfo->gga.altitude.scale;
            // 突然来个超大变化 异常值过滤
            if (abs(AGnssNmea->Altitude - altitudeBkp) > 100)
                AGnssNmea->Altitude = altitudeBkp; // 跳变就用上一次有效输出值填充
        }
        else
            AGnssNmea->Altitude = 0;

        altitudeBkp = AGnssNmea->Altitude; // 备份有效高度

        // 方向角
        if (AInfo->rmc.course.scale)
            AGnssNmea->Direction = AInfo->rmc.course.value / AInfo->rmc.course.scale;
        else
            AGnssNmea->Direction = 0;

        // 卫星数目统计 用于追踪卫星数量
        AGnssNmea->GnssTracked = AInfo->gga.satellites_tracked;

        // 每种定位类型 最多只有12个卫星参与追踪定位 参见GSA
        // GPS卫星数目
        AGnssNmea->GpsTracked = 0;
        for (Temp = 0; Temp < 12; Temp++)
        {
            if (AInfo->gsa[0].sats[Temp])
                AGnssNmea->GpsTracked++;
        }

        // 北斗卫星数目 GSA
        AGnssNmea->BdsTracked = 0;
        for (Temp = 0; Temp < 12; Temp++)
        {
            if (AInfo->gsa[1].sats[Temp])
                AGnssNmea->BdsTracked++;
        }

        result = true;
    }
    
    return result;
}

/* GPS 数据接收处理 */
static void AppEventGpsUartReceived(API_Event_t* pEvent)
{
    GPS_Info_t*  gpsInfo   = Gps_GetInfo();
    GnssEvent_t* gnssEvent = (GnssEvent_t*)OS_Malloc(sizeof(GnssEvent_t)); // 给事件接收方释放内存
    GnssNmea_t*  gnssNmea  = (GnssNmea_t*)OS_Malloc(sizeof(GnssNmea_t));   // 给事件接收方释放内存

    if (gnssNmea && gnssEvent)
    {
        // 解析串口得到的数据
        GPS_Update(pEvent->pParam1,pEvent->param1);

        // 数据转换
        Gnss_Original2Brief(gpsInfo, gnssNmea);

        gnssEvent->EventId   = kGnssEventNmeaReveived;
        gnssEvent->EventData = (void *)gnssNmea;      // 给事件接收方释放内存
        OS_SendEvent(GnssTaskHandle, gnssEvent, OS_TIME_OUT_WAIT_FOREVER, OS_EVENT_PRI_NORMAL);
    }
}

static void AppEventDispatch(API_Event_t* pEvent)
{
    switch(pEvent->id)
    {
        case API_EVENT_ID_POWER_ON:
            break;    
        case API_EVENT_ID_SYSTEM_READY:
            break;
        case API_EVENT_ID_NO_SIMCARD:
            break;
        case API_EVENT_ID_SIMCARD_DROP:
            break;              
        case API_EVENT_ID_GPS_UART_RECEIVED:
            AppEventGpsUartReceived(pEvent);
            break;
        case API_EVENT_ID_UART_RECEIVED:
            break;
        default:
            break;
    }
}

// 主任务 派发事件
static void main_TaskEntry(void *pData)
{
    API_Event_t* event = NULL;

    //open UART1 to print NMEA infomation
    UART_Config_t config = {
        .baudRate   = UART_BAUD_RATE_115200,
        .dataBits   = UART_DATA_BITS_8,
        .stopBits   = UART_STOP_BITS_1,
        .parity     = UART_PARITY_NONE,
        .rxCallback = NULL,
        .useEvent   = true
    };
    UART_Init(UART1, config);

    Oled_Init();

     // 创建 GPRS任务
    GprsTaskHandle = OS_CreateTask(Gprs_TaskEntry,
                                    NULL, 
                                    NULL, 
                                    GPRS_TASK_STACK_SIZE, 
                                    GPRS_TASK_PRIORITY, 
                                    0,
                                    0, 
                                    GPRS_TASK_NAME);     
    
    // 创建 GNSS任务
    GnssTaskHandle = OS_CreateTask(Gnss_TaskEntry,
                                    NULL, 
                                    NULL, 
                                    GPS_TASK_STACK_SIZE, 
                                    GPS_TASK_PRIORITY,
                                    0,
                                    0, 
                                    GPS_TASK_NAME);

    // 创建 Media任务
    MediaTaskHandle = OS_CreateTask(Media_TaskEntry,
                                    NULL, 
                                    NULL, 
                                    MEDIA_TASK_STACK_SIZE, 
                                    MEDIA_TASK_PRIORITY, 
                                    0,
                                    0, 
                                    MEDIA_TASK_NAME);
    // 创建 UI任务                              
    UiTaskHandle    = OS_CreateTask(Ui_TaskEntry,
                                    NULL, 
                                    NULL, 
                                    UI_TASK_STACK_SIZE, 
                                    UI_TASK_PRIORITY, 
                                    0,
                                    0, 
                                    UI_TASK_NAME);

    //Wait event
    while (1) {
        if (OS_WaitEvent(MainTaskHandle, (void**)&event, OS_TIME_OUT_WAIT_FOREVER)) {
            AppEventDispatch(event);
            OS_Free(event->pParam1);
            OS_Free(event->pParam2);
            OS_Free(event);
        }
    }
}

void app_Main(void)
{
    MainTaskHandle = OS_CreateTask(main_TaskEntry,        // 主任务任务函数
                                    NULL,                 // 传递给执行函数的参数
                                    NULL,                 // 任务堆栈地址，暂时不支持
                                    MAIN_TASK_STACK_SIZE, // 任务栈大小
                                    MAIN_TASK_PRIORITY,   // 任务优先级
                                    0,                    // 任务立即执行
                                    0,                    // 时间片，未用保留
                                    MAIN_TASK_NAME);      // 任务名称

    // 主任务用来接收来自底层的消息
    OS_SetUserMainHandle(&MainTaskHandle);
}
